/*
 * Created on 2007
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package com.XSQS.storage.manager;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.XSQS.storage.helper.FileName;
import com.XSQS.storage.thread.FPThread;
import com.XSQS.storage.thread.ThreadPool;
import com.XSQS.storage.util.*;

/**
 * @author Jofee.Chan
 */
public class FreePageManager {
    
    private static FreePageManager fpm;
    private FileManager fm;
    private DataFileManager dfm;
    
    //private RandomAccessFile[] raf;
    private InnerManager[] im;
    public final static int FPARRAYSIZE = 128;
    /** Creates a new instance of FreePageManager */
    private FreePageManager() {
        fm = FileManager.getInstance();
        dfm = DataFileManager.getInstance();
        
        //raf = new RandomAccessFile[3];
        im = new InnerManager[3];
        im[0] = new InnerManager(Page.DOCINFOPAGE);
        im[1] = new InnerManager(Page.DATAPAGE);
        im[2] = new InnerManager(Page.STRINGPAGE);
    }
    
    public static FreePageManager getInstance(){
        if(fpm == null){
            fpm = new FreePageManager();
            //System.out.println("new FreePageManager()");
        }
        return fpm;
    }
    
    
    public int applyFreeDocInfoPage(){
        return im[0].applyFirstFP();
    }
    
    public int applyFreeDataPage(){
        return im[1].applyFirstFP();
    }
    
    public int applyFreeStringPage(){
        return im[2].applyFirstFP();
    }

    //************************ For Test ********************************
    public int curFreeDocInfoPage(){
        return im[0].applyFirstFP();
    }
    
    public int curFreeDataPage(){
        return im[1].applyFirstFP();
    }
    
    public int curFreeStringPage(){
        return im[2].applyFirstFP();
    }
    
    //******************************************************************
    
    protected void setDocInfoPageUsed(int lpno){
        im[0].usePage(lpno);
    }
    
    protected void setDataPageUsed(int lpno){
        im[1].usePage(lpno);
    }
    
    protected void setStringPageUsed(int lpno){
        im[2].usePage(lpno);
    }
    
    protected int freeDocInfoPage(int lpno){
        return im[0].freePage(lpno);
    }
    
    protected int freeDataPage(int lpno){
        return im[1].freePage(lpno);
    }
    
    protected int freeStringPage(int lpno){
        return im[2].freePage(lpno);
    }
    
    
    public void save(){
        for(int i = 0; i < 3; i++){
            im[i].save();
        }
    }
    
    public class InnerManager{
        private RandomAccessFile raf;
        private ThreadPool tp;
        private FPThread fpt;
        private int[] freePage;
        private int type;
        private int usedPos, freePos;
        private boolean getNewPage;
        private final int threshold = 28;
        private final int ARRAYSIZE = 128;
        public InnerManager(int type){
            this.type = type;
            this.getNewPage = false;
            freePage = new int[ARRAYSIZE];
            initFreePage();
            usedPos = 0;
            for(freePos = 0; freePos < ARRAYSIZE; freePos++){
                if(freePage[freePos] != -1){
                    break;
                }
            }
            tp = ThreadPool.getInstance();
            fpt = new FPThread(this, this.type);
            tp.setFPThread(fpt);
        }
        
        private void openFile(){
            if(raf == null){
                raf = fm.openFileSolely(getFileName(this.type));
                try {
                    raf.seek(64);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
        
        private String getFileName(int type){
            switch(type){
                case Page.DOCINFOPAGE:
                    return FileName.getDocInfoFile();
                case Page.DATAPAGE:
                    return FileName.getDataFile();
                case Page.STRINGPAGE:
                    return FileName.getStringFile();
                default:
                    return null;
            }
        }
        
        private void initFreePage(){
            if(raf == null){
                openFile();
            }
            try {
                raf.seek(64);
                for(int i =0; i < ARRAYSIZE; i++){
                    freePage[i] = raf.readInt();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        
        public synchronized int applyFirstFP(){
            if(freePos >= this.ARRAYSIZE){
                try {
                    this.wait();
                    //return -1;
                } catch (InterruptedException ex) {
                    Logger.getLogger(FreePageManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }else if(freePos >= this.ARRAYSIZE - this.threshold){
                if(!getNewPage){
                    getNewPage = true;
                    getFreePage(freePage[this.ARRAYSIZE - 1], freePos - usedPos);
                }
                    
            }
            int lpno = freePage[freePos];
            freePage[usedPos] = freePage[freePos];
            if(freePos != usedPos) freePage[freePos] = -1;
            usedPos ++;
            freePos ++;
            return lpno;
        }
        
        public synchronized void usePage(int lpno){
            for(int i = 0; i < usedPos; i++){
                if(freePage[i] == lpno){
                    for(int j = i+1; j < usedPos; j++){
                        freePage[j-1] = freePage[j];
                    }
                    usedPos --;
                    freePage[usedPos] = -1;
                    return;
                }
            }
            //����ҳʹ��������û��lpno��Ӧ��ҳ��
            System.err.println("[FreePageManager.usePage] Page: " + lpno + " hasn't beean applied! ");
        }
        
        public synchronized int freePage(int lpno){
            if(lpno <= 0){
                System.err.println("FreePageManager.freePage() : Invalid Param: " + lpno);
                return -1;
            }
            int next = freePage[freePos];
            if(usedPos < freePos){
                freePage[freePos -1] = lpno;
                freePos --;
            }else if(usedPos == freePos){
                for(int i = ARRAYSIZE-1; i > usedPos; i++){
                    freePage[i] = freePage[i-1];
                }
                freePage[freePos] = lpno;
            }else{
                System.err.println("Fatal Error!!Code: fpmu>f");
                return -1;
            }
            return next;
        }
        
        private void getFreePage(int lpno, int num){
            //System.out.println("getFreepage: " + lpno + " " + num);
            fpt.setParam(lpno, num);
        }
        
        public synchronized void fillFreePage(int[] newPages){
            //System.out.println("fillFreepage: " + newPages.length);
            int ns = newPages.length;
            int space = freePos - usedPos;
            int left = ARRAYSIZE - freePos;
            int start = usedPos;
            int num = ns;
            if(space >= ns){
                start += space - ns;
            }else{
                if(space == 0){
                    return;
                }else if(space > 0){
                    ns = space;
                }else{
                    System.err.println("Fatal Error! freePos<usedPos");
                }
            }
            for(int i = 0; i < left; i++){
                freePage[start + i] = freePage[freePos + i];
            }
            freePos = start;
            start += left;
            for(int i = 0; i < num; i++){
                freePage[start + i] = newPages[i];
            }
            getNewPage = false;
            this.notifyAll();
        }
        
        public int getCurFirstFP(){
            return freePage[freePos];
        }
        
        public synchronized void save(){
            if(raf == null){
                openFile();
            }
            try {
                raf.seek(64);
                int i;
                for(i = usedPos; i < freePos; i++){
                    //freePage[i] = -1;
                    raf.writeInt(freePage[i]);
                }
                for(i = 0; i < usedPos; i++){
                    raf.writeInt(freePage[i]);
                }
                for(i = freePos; i < this.ARRAYSIZE; i++){
                    raf.writeInt(freePage[i]);
                }
//                System.out.println("FreePageManager[" + type + "] Saved Successfully");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}
